package butines.core.math;

public class LineSegment {
	
	private Vector2f pointA;
	private Vector2f pointB;
	private Vector2f pointIntersection = new Vector2f();
	
	public boolean checkIntersection(Vector2f pointCircle, float radius) {
		Vector2f forward = new Vector2f();
		forward.sub(pointB, pointA);
		forward.normalise();

		Vector2f side = new Vector2f(-forward.y, forward.x);

		// Move o circulo para dentro do local space definido pelo segmento BA com a origem em A.
		Transformation transformation = new Transformation();
		transformation.setPosition(pointA);
		transformation.setForward(forward);
		transformation.setSide(side);
		
		Vector2f localPoint = transformation.toLocalPoint(pointCircle);

		// If the local position + the radius is negative then the circle lays behind point A so there is no intersection possible.
		if (localPoint.x + radius < 0) {
			return false;
		}
		
		// If the local x pos minus the radius is greater than length A-B then the circle cannot intersect the line segment
		float lenSq = (localPoint.x - radius) * (localPoint.x - radius);
		if (lenSq > Vector2f.distanceSq(pointB, pointA)) {
			return false;
		}
		
		// if the distance from the x axis to the object's position is less than its radius then there is a potential intersection.
		if (Math.abs(localPoint.y) > radius) {
			return false;
		}
		
		// now to do a line/circle intersection test. The center of the circle is represented by A, B.
		// The intersection points are given by the formulae x = A +/-sqrt(r^2-B^2), y=0.
		// We only need to look at the smallest positive value of x.
		float a = localPoint.x;
		float b = localPoint.y;

		float ip = a - (float) Math.sqrt(radius * radius - b * b);

		if (ip <= 0) {
			ip = a + (float) Math.sqrt(radius * radius - b * b);
		}

		Vector2f.multAdd(forward, ip, pointIntersection);
		pointIntersection.add(pointA);

		return true;
	}
	
	public float distance(Vector2f point) {
		// if the angle is obtuse between PA and AB is obtuse then the closest vertex must be A
		float dotA = (point.x - pointA.x) * (pointB.x - pointA.x) + (point.y - pointA.y) * (pointB.y - pointA.y);

		if (dotA <= 0) {
			return Vector2f.distance(pointA, point);
		}

		//if the angle is obtuse between PB and AB is obtuse then the closest vertex must be B
		float dotB = (point.x - pointB.x) * (pointA.x - pointB.x) + (point.y - pointB.y) * (pointA.y - pointB.y);

		if (dotB <= 0) {
			return Vector2f.distance(pointB, point);
		}

		//calculate the point along AB that is the closest to P
		//A + ((B - A) * dotA)/(dotA + dotB)
		Vector2f pointClosest = new Vector2f();
		
		// (B - A) * dotA
		pointClosest.sub(pointB, pointA);
		pointClosest.mult(dotA);
		
		//  / (dotA + dotB)
		pointClosest.div(dotA + dotB);
		
		// A +
		pointClosest.add(pointA);

		return Vector2f.distance(point, pointClosest);
	}

	public boolean checkIntersection(Vector2f point) {
		// if the angle is obtuse between PA and AB is obtuse then the closest vertex must be A
		float dotA = (point.x - pointA.x) * (pointB.x - pointA.x) + (point.y - pointA.y) * (pointB.y - pointA.y);

		if (dotA <= 0) {
			pointIntersection.add(pointA);
			return false;
		}

		//if the angle is obtuse between PB and AB is obtuse then the closest vertex must be B
		float dotB = (point.x - pointB.x) * (pointA.x - pointB.x) + (point.y - pointB.y) * (pointA.y - pointB.y);

		if (dotB <= 0) {
			pointIntersection.add(pointB);
			return false;
		}

		//calculate the point along AB that is the closest to P
		//A + ((B - A) * dotA)/(dotA + dotB)
		
		// (B - A) * dotA
		pointIntersection.sub(pointB, pointA);
		pointIntersection.mult(dotA);
		
		//  / (dotA + dotB)
		pointIntersection.div(dotA + dotB);
		
		// A +
		pointIntersection.add(pointA);
		
		return true;
	}

	public Vector2f getPointA() {
		return pointA;
	}

	public void setPointA(Vector2f pointA) {
		this.pointA = pointA;
	}

	public Vector2f getPointB() {
		return pointB;
	}

	public void setPointB(Vector2f pointB) {
		this.pointB = pointB;
	}

	public Vector2f getPointIntersection() {
		return pointIntersection;
	}
	
}
